#include "../h/iLogger.h"
#include "iIOR.h"
#include "iIO.h"

#if defined(__WIN__)
#include <WinSock.h>
#elif defined(__LINUX__)
#include <sys/select.h>
#include <sys/epoll.h>
#endif

#define DEBUG(x) iDEBUG(MOD_UTIL,x)
#define ERROR(x) iERROR(MOD_UTIL,x)
int iIOSelect::run()
{
	fd_set read_fds, write_fds, exception_fds;
	FD_ZERO(&read_fds);
	FD_ZERO(&write_fds);
	FD_ZERO(&exception_fds);

	static os_io_t s_fd = socket(AF_INET, SOCK_STREAM, 0);
	os_io_t fd = -1;
	for(std::list<iIO*>::iterator iter=m_ios.begin();iter!=m_ios.end();++iter)
	{
		iIO* io = *iter;
		fd = fd < io->get_io() ? io->get_io():fd;
		if(io->get_io_mask() & (iIO::IO_MASK_READ|iIO::IO_MASK_ACCEPT|iIO::IO_MASK_CONNECT))
			FD_SET(io->get_io(), &read_fds);
		if(io->get_io_mask() & iIO::IO_MASK_WRITE)
			FD_SET(io->get_io(), &write_fds);
		if(io->get_io_mask() & iIO::IO_MASK_CONNECT)
			FD_SET(io->get_io(), &exception_fds);
	}
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 10000;
	if(fd == -1)
	{
        #if defined(__WIN__)
		fd = s_fd;
		FD_SET(fd, &read_fds);
        #elif defined(__LINUX__)
        ::select(0, NULL, NULL, NULL, &tv);
        return 0;
        #endif
	}
	int ret_fds = ::select(fd+1, &read_fds, &write_fds, &exception_fds, &tv);
	switch(ret_fds)
	{
	case -1: //error
		break;
	case 0:  //timeout
		break;
	default:
		{
			for(std::list<iIO*>::iterator iter=m_ios.begin();iter!=m_ios.end();++iter)
			{
				iIO* io = *iter;
				if(FD_ISSET(io->get_io(), &exception_fds))
					io->on_error();
				if(FD_ISSET(io->get_io(), &write_fds))
					io->on_write();
				if(FD_ISSET(io->get_io(), &read_fds))
					io->on_read();
			}
		}
		break;
	}

	return 0;
}

#if defined(__LINUX__)
iIOEPoll::iIOEPoll()
:m_epoll(-1)
,m_evs(NULL)
{
    m_epoll = epoll_create(EPOLL_SLOT_SIZE);
    m_evs = new epoll_event[EPOLL_SLOT_SIZE];
    memset(m_evs, 0, sizeof(epoll_event)*EPOLL_SLOT_SIZE);
}
void iIOEPoll::on_io_join(iIO* io)
{
    if(-1 == m_epoll)
        return;

    if(NULL == io)
        return;

    epoll_event *ee = new epoll_event();
    ee->events = 0;
    ee->data.ptr = io;
    if(io->get_io_mask() & (iIO::IO_MASK_READ|iIO::IO_MASK_ACCEPT|iIO::IO_MASK_CONNECT))
        ee->events |= EPOLLIN;
    if(io->get_io_mask() & iIO::IO_MASK_WRITE)
        ee->events |= EPOLLOUT;
    if(io->get_io_mask() & iIO::IO_MASK_CONNECT)
        ee->events |= EPOLLERR;
    ee->events |= EPOLLHUP;
    if(epoll_ctl(m_epoll, EPOLL_CTL_ADD, io->get_io(), ee) == -1)
    {
        delete ee;
        return;
    }
    m_evh.insert(std::make_pair(ee->data.fd, ee));
    m_ios.insert(std::make_pair(ee->data.fd, io));
    DEBUG("epoll io join,fd="<<io->get_io());
}
void iIOEPoll::on_io_quit(iIO* io)
{
    if(-1 == m_epoll)
        return;

    if(NULL == io)
        return;

    std::map<int,epoll_event*>::iterator iter = m_evh.find(io->get_io());
    if(iter == m_evh.end())
        return;
    epoll_event* ee = iter->second;
    if(-1 == epoll_ctl(m_epoll, EPOLL_CTL_DEL, io->get_io(), ee))
        return;
    m_evh.erase(iter);
    m_ios.erase(ee->data.fd);
    delete ee;
    DEBUG("epoll io quit,fd="<<io->get_io());
}
int iIOEPoll::run()
{
    if(m_epoll == -1)
        return -1;

    int fds = epoll_wait(m_epoll, m_evs, EPOLL_SLOT_SIZE, 10);
    if(fds == -1)
    {
        ERROR("epoll_wait error="<<errno);
        return -1;
    }

    for(int i=0; i<fds; i++)
    {
        if(m_evs[i].events & EPOLLIN)
        {
            DEBUG("epoll read event detected");
            ((iIO*)m_evs[i].data.ptr)->on_read();
        }
        if(m_evs[i].events & EPOLLOUT)
        {
            DEBUG("epoll write event detected");
            ((iIO*)m_evs[i].data.ptr)->on_write();
        }
        if(m_evs[i].events & EPOLLERR)
        {
            DEBUG("epoll error event detected");
            ((iIO*)m_evs[i].data.ptr)->on_error();
        }
    }
}
#endif
